package com.sy.filter;


import com.alibaba.fastjson.JSON;
import com.sy.bean.HttpOperation;
import com.sy.bean.JwtOperation;
import com.sy.dto.Result;
import com.sy.dto.TokenUser;
import com.sy.utils.ApplicationContextUtils;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 这个过滤器用于拦截每次需要携带Token的请求
 * 对Token进行统一的检查
 */
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private JwtOperation jwtOperation;
    private UserDetailsService userDetailsService;
    private PasswordEncoder encoder;
    private HttpOperation httpOperation;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        //由于这里的Filter不是SpringBean（也千万不要注册为SpringBean）
        //无法@Autowired注入，因此通过Spring容器感知器对象获取容器对象然后获取Bean对象
        jwtOperation = ApplicationContextUtils.getBean(JwtOperation.class);
        userDetailsService = ApplicationContextUtils.getBean("cacheDetailService", UserDetailsService.class);
        encoder = ApplicationContextUtils.getBean(BCryptPasswordEncoder.class);
        httpOperation = ApplicationContextUtils.getBean(HttpOperation.class);

        //获取请求头中的token
        String token = request.getHeader("auth-token");
        //这里的校验合法性操作应当在业务层中完成
        Result result = new Result();
        try {
            //头信息中没有token
            if (StringUtils.isBlank(token)) {
                throw new BadCredentialsException("您的登录信息已失效，请重新登录");
            }
            //获取Claims
            Claims claims = jwtOperation.parseJwt(token);
            Object userClaim = claims.get("user");
            //Token格式无效
            if (null == claims || null == userClaim) {
                throw new BadCredentialsException("您的登录信息已失效，请重新登录");
            }
            //获取用户信息
            TokenUser user = JSON.parseObject(String.valueOf(claims.get("user")), TokenUser.class);
            String username = user.getUsername();
            //这里获取的是JWT中存的密文密码
            String pwd = user.getPassword();
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                //数据库获取当前用户信息
                //这里的方法如果之前调用过会存在缓存
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                //数据库密码和JWT密码不一致，说明密码可能被篡改了
                if (!StringUtils.equals(pwd, userDetails.getPassword())) {
                    throw new BadCredentialsException("您的密码发生了变化，为保障安全请您重新登录");
                }

                //构建认证用户及权限对象
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        username, null, userDetails.getAuthorities());
                //将权限写入本次会话
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            //执行过滤链中后续的过滤器
            chain.doFilter(request, response);

        } catch (Exception e) {
            throw e;
        }
    }
}